#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "types.h"
#include "sim.h"

static int R0;
static int R1;

extern instrp list_curp;

extern char rmacro_content[MAX_MACRO][MAX_CODE_LENGTH];
int recognize_rmacro(char * str,int n);

static int extern_memory[RAM_SIZE];

extern int flag_debug;
extern int flag_montre;

int op_flag = 0;

int do_load(char * left, char * right,func_callp fc){
	int * adr = NULL;
	int val;
	int ram_adr;
	int k;
	if ( strcmp(right,"R0") == 0 ){
		adr = &R0;
	}else if (strcmp(right, "R1") == 0 ){
		adr = &R1;
	}else{
		printf("Line %d: Right argument must be a register address\n",list_curp->source_code_line_no);
		return -1;
	}

	//is it a numeral #?
	if ( left[0] == '#' ){
		if ( !sscanf(left+1,"%d",&val) ){
			printf("Line %d: You forgot to write a number\n",list_curp->source_code_line_no);
			return -1;
		}
	*adr = val;
	}else if( sscanf(left,"%d", &ram_adr)){
			//ok it's a number
			*adr = extern_memory[ram_adr];
	}else{
		//now left arg must be a define-macro
		if ( (k = recognize_rmacro(left,strlen(left))) == -1 ){
			printf("Line %d: Unknown define-macro\n",list_curp->source_code_line_no);
			return -1;
		}
		if( !sscanf(rmacro_content[k],"%d",&ram_adr) ){
			printf("Line %d: Left argument should be a number\n",list_curp->source_code_line_no);
			return -1;
		}
		*adr = extern_memory[ram_adr];
	}
	return 0;
}
int do_store(char * left, char * right, func_callp fc){
	int * adr = NULL;
	int ram_adr;
	int k;
	if ( strcmp(left,"R0") == 0 ){
		adr = &R0;
	}else if (strcmp(left, "R1") == 0 ){
		adr = &R1;
	}else{
		printf("Line %d: Left parameter must be a register address\n",list_curp->source_code_line_no);
		return -1;
	}

	//try to scan a number. if you can't then it is a define-macro
	if ( !sscanf(right,"%d",&ram_adr) ){
		//now right arg must be a define-macro
		if ( (k = recognize_rmacro(right,strlen(right))) == -1 ){
			printf("Line %d: Unknown define-macro\n",list_curp->source_code_line_no);
			return -1;
		}
		if( !sscanf(rmacro_content[k],"%d",&ram_adr) ){
			printf("Line %d: Right argument should be a number\n",list_curp->source_code_line_no);
			return -1;
		}
	}
	extern_memory[ram_adr] = *adr;
	return 0;
}
int do_not(char * op){
	int * adr;
	if ( !strcmp(op,"R0") ){
		adr = &R0;
	}else if ( !strcmp(op, "R1")){
		adr = &R1;
	}else{
		printf("Line %d: Right parameter must be a register address\n",list_curp->source_code_line_no);
		return -1;
	}
	*adr = ~*adr;
	return 0;
}
int sim_getR1(){
	return R1;
}
void sim_display(){
	int i;
	if (flag_debug){
		printf("===========================\n");
		printf("R0: %d\nR1: %d\n",R0,R1);
		for (i = 0; i <RAM_SIZE; i++){
			printf("%d|",extern_memory[i]);
		}
		printf("\nOp-flag: %d\n\n",op_flag);

	}
	if ( flag_montre){
		printf("Time %d:%d:%d Date %d/%d/%d Day %d\n",extern_memory[16],extern_memory[17],extern_memory[18],extern_memory[15],extern_memory[14],extern_memory[13],extern_memory[19]);
	}
}



int do_opcode0(enum opcode op){
	int t;
	op_flag = 0;
	switch (op){
	case NOP:
		break;
	case ADD:
		R1 = R0 + R1;
		if ( R1 > 255) op_flag = 1;
		R1 %= 256;
		break;
	case MULT:
		R1 = R0 * R1;
		if ( R1 > 255 ) op_flag = 1;
		R1 %= 256;
		break;
	case AND:
		R1 = R0 & R1;
		break;
	case CMP:
		R1 = R0 == R1;
		break;
	case SWC:
		t = R1;
		R1 = R0;
		R0 = t;
	default:
		break;
	}
	return 0;
}
int do_opcode1(enum opcode op, char * operand){
	switch (op){
		case NOT:
		if ( do_not(operand) ) return -1;
		break;
		default:;
	}
	return 0;
}
int do_opcode2(enum opcode op, char * left, char * right, func_callp fc){
	switch (op){
		case STORE:
			if ( do_store(left,right,fc) ) return -1;
			break;
		case LOAD:
			if ( do_load(left,right,fc) ) return -1;
			break;
		default:
			break;
	}
	return 0;
}

